package com.blockmeta.bbs.businesslibrary.websocket;


import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class SortTable<K, V>
{
	public enum UpdateMode
	{
		REFRESH,	// 全部更新
		INSERT,
		DELETE,
		UPDATE,		// 更新
		REPLACE,	// 更新, 且對調順序
		IGNORE,		// 忽略此次更新
		CLEAR		// 全部清除
	};

	public class KeyStatus<V>
	{
		public UpdateMode 	m_status;	// 更新狀態
		public int 		 	m_row;		// 更新第幾筆
		public int 		 	m_lastRow;	// 前次第幾筆(REPLACE用)
		public V 		 	m_record;	// 該筆Value

		public KeyStatus() { m_status = UpdateMode.IGNORE; m_record = null; }
	}

	protected ArrayList<V> m_arValues; // UI的順序
	protected HashMap<K, V> m_mapKeyValues; // 查表用
	protected ReentrantReadWriteLock m_lock;
	protected Comparator<? super V> m_comparator;

	public SortTable()
	{
		m_lock = new ReentrantReadWriteLock();
		m_arValues = new ArrayList<V>();
		m_mapKeyValues = new HashMap<K, V>();
	}

	@Deprecated
	public void setComparator(Comparator<? super V> comparator)
	{
//		m_comparator = comparator;
	}

	public boolean containsKey(K key)
	{				
		try
		{
			m_lock.readLock().lock();
			return m_mapKeyValues.containsKey(key);
		}
		finally
		{
			m_lock.readLock().unlock();
		}			
	}

	public void sort()
	{
		try
		{
			m_lock.writeLock().lock();

			if (m_comparator != null)
			{
				try
				{
					System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
					Collections.sort(m_arValues, m_comparator);
				}
				catch(Exception ex)
				{
				}
			}
		}
		finally
		{
			m_lock.writeLock().unlock();
		}
	}

	protected int getVIndex(V value)
	{
		int index;
		try
		{
			m_lock.readLock().lock();

			if (m_comparator != null)
			{
				index = Collections.binarySearch(m_arValues, value, m_comparator);
			}
			else
			{
				index = m_arValues.indexOf(value);
			}
		}
		finally
		{
			m_lock.readLock().unlock();
		}
		return index;
	}

	public KeyStatus<V> put(K key, V value)
	{
		KeyStatus<V> status = new KeyStatus<V>();

		try
		{
			m_lock.writeLock().lock();

			V tempV = m_mapKeyValues.get(key);
			int pos;

			if (tempV == null)
			{
				m_arValues.add(value);
				sort();
				pos = getVIndex(value);

				status.m_status = UpdateMode.INSERT;
				status.m_row = pos;
				status.m_record = value;
			}
			else
			{
				pos = getVIndex(tempV);

				if (pos >= 0)
				{
					m_arValues.set(pos, value);
					sort();

					int row = getVIndex(value);

					if (pos == row)
					{
						status.m_status = UpdateMode.UPDATE;
						status.m_row = pos;
						status.m_record = value;
					}
					else
					{
						status.m_status = UpdateMode.REPLACE;
						status.m_row = row;
						status.m_lastRow = pos;
						status.m_record = value;
					}
				}
			}
			m_mapKeyValues.put(key, value);
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
		finally
		{
			m_lock.writeLock().unlock();
		}

		return status;
	}

	public Set<K> keySet()
	{				
		try
		{
			m_lock.writeLock().lock();			
			return new HashSet<K>(m_mapKeyValues.keySet());
		}
		finally
		{
			m_lock.writeLock().unlock();
		}		
	}
	
	public int getIndex(K key)
	{
		int index = -1;
		try
		{
			m_lock.readLock().lock();
			V value = m_mapKeyValues.get(key);
			if (value != null)
			{
				index = this.getVIndex(value);
			}
		}
		finally
		{
			m_lock.readLock().unlock();
		}
		return index;
	}

	public V getByKey(K key)
	{
		try
		{
			m_lock.readLock().lock();
			return m_mapKeyValues.get(key);
		}
		finally
		{
			m_lock.readLock().unlock();
		}
		
	}

	public V getByIndex(int index)
	{
		try
		{
			m_lock.readLock().lock();
			if (index >= 0 && index < m_arValues.size())
				return m_arValues.get(index);
			return null;
		}
		finally
		{
			m_lock.readLock().unlock();
		}
	}

	public K getKey(int index)
	{
		K key = null;
		try
		{
			m_lock.readLock().lock();

			if (index >= 0 && index < m_arValues.size())
			{
				V value = m_arValues.get(index);
				for (Entry<K, V> ii : m_mapKeyValues.entrySet())
				{
					if (ii.getValue() == value)
					{
						key = ii.getKey();
						break;
					}
				}
			}
		}
		finally
		{
			m_lock.readLock().unlock();
		}

		return key;
	}

	public boolean exist(K key)
	{		
		try
		{
			m_lock.readLock().lock();
			return m_mapKeyValues.get(key) != null;
		}
		finally
		{
			m_lock.readLock().unlock();
		}
	}

	public KeyStatus<V> remove(K key)
	{
		KeyStatus<V> ret = new KeyStatus<V>();

		try
		{
			m_lock.writeLock().lock();

			V value = m_mapKeyValues.remove(key);

			if (value != null)
			{
				int row = this.getVIndex(value);

				if (row < 0 || value != m_arValues.get(row))
				{
					m_arValues.remove(value);
				}
				else
				{
					m_arValues.remove(row);
				}

				ret.m_status = UpdateMode.DELETE;
				ret.m_row = row;
				ret.m_record = value;
			}
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
		finally
		{
			m_lock.writeLock().unlock();
		}
		return ret;
	}

	public int size()
	{		
		try
		{
			m_lock.readLock().lock();
			return m_arValues.size();
		}
		finally
		{
			m_lock.readLock().unlock();
		}
	}

	public void clear()
	{		
		try
		{
			m_lock.writeLock().lock();
			m_arValues.clear();
			m_mapKeyValues.clear();
		}
		finally
		{
			m_lock.writeLock().unlock();
		}
	}

	public ArrayList<V> getArrayList()
	{		
		try
		{
			m_lock.readLock().lock();
			return new ArrayList<>(m_arValues);
		}
		finally
		{
			m_lock.readLock().unlock();
		}
	}
}
